package com.cangshi.permission.filter;

import com.alibaba.fastjson.JSON;
import com.cangshi.permission.entity.ObjectSources;
import com.cangshi.permission.entity.Subject;
import com.cangshi.permission.entity.UserSubject;
import com.cangshi.permission.exception.CheckFailedException;
import com.cangshi.permission.exception.PermissionException;
import com.cangshi.permission.exception.PermissionInitException;
import com.cangshi.permission.holder.CurrentHolder;
import com.cangshi.permission.session.exception.SessionCannotFindException;
import com.cangshi.permission.session.exception.SessionIdCannotFindException;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * Created by Eoly on 2017/4/1.
 */
public abstract class PermissionFilter implements Filter {

    private ObjectSources objectSources;

    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;
        response.setHeader("Content-Type", "text/html;charset=UTF-8");

        if (objectSources == null) {
            // 抛错 未在xml中设置
            throw new PermissionInitException("服务端未在xml中初始化objectSources");
        }

        // 使用数据源实例化当前登陆用户
        UserSubject currentUser = new UserSubject(objectSources);

        // 存入线程缓存
        CurrentHolder.setSubject(currentUser);

        Boolean isAccessible = false;
        // 拦截事件
        try {
            isAccessible = isAccessible(request, response);
        } catch (PermissionException e) {

            // 更新当前用户属性
            currentUser = (UserSubject) CurrentHolder.getSubject();

            // 在审核证书失败的情况下，用户具有默认权限，即可以访问默认权限所对应的url
            if (currentUser.couldAccessing(request.getServletPath())) {
                doWhileSuccess(request, response, chain);
            } else {
                doWhileFail(request, response, chain, e);
            }

        }
        if (isAccessible) {
            doWhileSuccess(request, response, chain);
        } else {
            doWhileFail(request, response, chain, new CheckFailedException("该用户没有权限访问当前url"));
        }

    }

    protected Boolean isAccessible(HttpServletRequest request, HttpServletResponse response) {

        // 获取请求头的authorization信息
        String authorization = request.getHeader(objectSources.getDefaultHeaderName());

        // 获取当前用户，并设置authorization信息
        Subject currentUser = CurrentHolder.getSubject();

        // 设置用户的验证信息
        ((UserSubject) currentUser).setAuthorizationToken(authorization);
        ((UserSubject) currentUser).setAuthorizationEntityClass(objectSources.getEntityClass());

        // 避免存入线程动作被截止
        try {
            currentUser.buildAuthorizationEntity();
        } catch (PermissionException e) { // 捕获所有处理所得的错误
            // 抛出证书验证失败错误
            if (e instanceof CheckFailedException) {
                throw new CheckFailedException(e.getMessage());
            } else if (e instanceof SessionIdCannotFindException) {
                throw new SessionIdCannotFindException(e.getMessage());
            } else if (e instanceof SessionCannotFindException) {
                throw new SessionCannotFindException(e.getMessage());
            } else {
                throw new PermissionException(e.getMessage());
            }

        } finally {
            // 将当前用户重新存入线程缓存中，更新其属性
            CurrentHolder.removeSubject();
            CurrentHolder.setSubject(currentUser);
        }
        // 判断当前请求是否允许访问
        return currentUser.couldAccessing(request.getServletPath());
    }

    protected abstract void doWhileFail(HttpServletRequest request, HttpServletResponse response, FilterChain chain, PermissionException e) throws IOException, ServletException;

    protected abstract void doWhileSuccess(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException;


    public void init(FilterConfig filterConfig) {
    }

    public void destroy() {
    }

    public ObjectSources getObjectSources() {
        return objectSources;
    }

    public void setObjectSources(ObjectSources objectSources) {
        this.objectSources = objectSources;
    }
}
